Topics
This tutorial will demonstrate how to analyse audio data for acoustic
phonetic studies in R. It is mainly intended to demonstrated possible
workflows. The topics covered are:
- Phonetic databases, the case of
emuR and
EMU-SDMS, the EMU Speech Database Management System
- Sample data: the LOD database
- Queries and requeries
- Inspect the database:
serve()
- Calculate duration for vowel categories
- Vowels formants and visualisations with
ggplot2
- Calculating the Pillai distance
- Vowel explorer
Preamble
The generated HTML page of this tutorial is available here.
This tutorial is organised as an R Markdown notebook. To execute
the code within this notebook (filename Lecture.Rmd) it has
to be opened in RStudio. When executing code, the results appear beneath
the code. In order to do so, you need to have R and RStudio installed.
When this R notebook is loaded into RStudio, you can execute chunks by
clicking the Run button within the chunk or by placing your
cursor inside it and pressing Cmd+Shift+Enter, allowing you to
experiment with the code. This handout contains all output of the code
(tables, visualisations etc.). The easiest way to work with this R code
is to clone the entire project from this GitHub repository.
The following libraries will be needed and have to be installed.
library(tidyverse)
library(emuR)
library(tools)
library(rio)
library(ggplot2)
library(magrittr)
library(htmltools)
library(joeyr)
library(knitr)
Forced alignment
Before starting to work with the database, let’s have a quick
overview how the automatic segmentation of audio files into smaller
chunks like words and phonetic segments actually is working.
We are using the MAUS
tools from the University of Munich. Next to a web interface, MAUS
segmentation can be called directly from within R through an API.
The input is a stream of audio recording alongside of a written
representation of the words spoken in this recording. A trained model of
the language, here: Luxembourgish, then is used to map the sounds and
words derived from the text to the speech signal. The output is an
object with temporal alignments between words and sounds. This is a weak
form of speech recognition.
Text for a test of forced alignment: Den ale Mann ass an dat
kaalt Waasser gefall. (The old man fell into th cold water.) with
the corresponding sound file save together in the same directory.
# create an emuDB from this text + audio
convert_txtCollection(dbName = "testDb",
sourceDir = "./testDB",
targetDir = ".")
# load this db
db <- load_emuDB("testDb_emuDB")
INFO: Loading EMU database from testDb_emuDB... (1 bundles found)
|
| | 0%
|
|==============================================================================================================| 100%
# run forced aligment using BAS MAUS tools in R
runBASwebservice_all(db,
transcriptionAttributeDefinitionName = "transcription",
language ="ltz-LU",
verbose = TRUE,
runMINNI = FALSE,
resume = TRUE,
patience = 3)
Show a summary of the database.
# display the overview of the structure and content
summary(db)
── Summary of emuDB ────────────────────────────────────────────────────────────────────────────────────────────────────
Name: testDb
UUID: 93d5a88c-897b-4a1b-a4b5-f771b107b7df
Directory: /Users/peter.gilles/Documents/GitHub/Data_Science_Humanities/testDb_emuDB
Session count: 1
Bundle count: 1
Annotation item count: 53
Label count: 72
Link count: 50
── Database configuration ──────────────────────────────────────────────────────────────────────────────────────────────
── SSFF track definitions ──
data frame with 0 columns and 0 rows
── Level definitions ──
name type nrOfAttrDefs attrDefNames
bundle ITEM 2 bundle; transcription;
ORT ITEM 3 ORT; KAN; KAS;
MAU SEGMENT 1 MAU;
MAS ITEM 1 MAS;
── Link definitions ──
type superlevelName sublevelName
ONE_TO_MANY bundle ORT
ONE_TO_MANY ORT MAS
ONE_TO_MANY MAS MAU
The result of the query can also be displayed in the EMU Speech
Database Management System.
# open the database in a webviewer
serve(db)
The LOD database
The database contains the audio recordings from the
Lëtzebuerger Online Dictionnaire (available here, spoken by one female
speaker. The audio files have been automatically segmented with the MAUS
tools. We thus have a database conisting of textual data, basically
words, and the corresponding audio data. The audio data is segmented
into words and phonetic segments (sounds).
This database has been created beforehand. Infos how to create such a
database is explained in the EMU-SDMS
manual.
While we will be working in this tutorial with the full database
(26.000 recordings), a small demo database lod_emuDB has
been created with 500 recordings which can be used for individual
testing.
Loading a database
We start with loading the database and give an overview of structure
and content.
# load database
db = load_emuDB("/Users/peter.gilles/Documents/_Daten/LOD-emuDB/lod_emuDB")
# for testing purposes, use this smaller database in the Github folder
#db = load_emuDB("lod_emuDB")
# display the overview of the structure and content
summary(db)
Tracks in emuR are acoustic representation of the speech
signal, here dft for the waveform (time-amplitude
representation) and praatFms for the formant measures of
vowels (see below).
Levels in an emuR database stand for level of
interlinked linguistic information. bundle is the entire
audio file, ORT stands for the orthographical
representation of the audio file segmented in its single words.
MAU is the segmentation of all phonetic segment (=sounds)
of all ORT segments in bundle.
The hierarchical structure of these levels is expressed in the
link definitions as ONE-TO-MANY.
Database queries
An emuR database can be queried with a powerful query engine. The
first example is a simple query for one word, Aarbecht.
sl = query(db, query = "[ ORT == 'Aarbecht']")
sl
The result is a segment list (sl),
containing various information about the found item (time, level, name,
database info). The result of the query can also be displayed in the EMU
Speech Database Management System.
serve(db, seglist = sl)
The GUI will open in the Viewer pane of RStudio or you
can open it in a browser (Chrome preferred).
Here we can also display the hierarchical structure for this database
item, which is accessed during queries. bundle is the
top-level, representing the entire audio file.
The ORT level contains the nodes for the individual
words in the bundle, here the two words
Aarbecht and Aarbechten. The dependent level
then is MAU (=Munich Automatic Unit)
representing the single sounds of the words in ORT.
Two aspects render emuR query system extremely powerful: the use of
regular expressions (including negation and other extensions) and the
combined query on different levels of the database.
Let’s try more complex queries:
Regular expression, operator =~, words beginning with
Aarbecht...
sl = query(db, query = "[ ORT =~ 'Aarbecht.*']")
sl
Query: Select the vowel \[aː\] in
all words beginning with Aarbecht… Note that in the segment
list the label now has changed to the vowel and the respective start-end
information is now only for this sound \[aː\].
sl = query(db, query = "[ ORT =~ 'Aarbecht.*' ^ #MAU=='aː']")
sl
Query a sequence of sounds, e.g. e followed by
k (Méck), by using the sequence operator
->.
sl = query(db, query = "[ MAU == e -> MAU == k ]")
# print only the first 100 rows
sl[1:100, ]
In the sequence e->k, query only the vowel. Use the
result modifier #.
sl = query(db, query = "[ #MAU == e -> MAU == k ]")
# print only the first 100 rows
sl[1:100, ]
Wuery all sound items that occur at the end of a word and are \[p\].
sl <- query(db, query = "[End(ORT, MAU) == TRUE & MAU == p ]")
sl
Retrieve all word that contain five segments.
sl <- query(db, "[Num(ORT, MAU) == 5]")
sl
Using requery, the results from a previous query can be
further specified.
# requery_seq()
# query all "m" phonetic items
sl_m = query(db, "MAU == m")
# sequential requery (left shift result by 1 (== offset of -1))
# and hence retrieve all phonetic items directly preceeding
# all "m" phonetic items
sl_req_n <- requery_seq(db,
seglist = sl_m,
offset = -1)
sl_req_n
Groups of sounds can be grouped together to
label groups: - all long monophthongs:
"iː", "uː", "aː", "oː", "ɔː", "ɛː", "eː" - all short
monophthongs: "i", "u", "ɑ", "o", "æ", "e", "ə", "ɐ"
# query all long monophthongs
sl = query(db, "MAU == longMonophthongs")
sl
With the query the user can compile a data frame from the database
which then forms the subset for further phonetic analysis. We can select
e.g. all instances of certain (or all) vowels, specifying the context
before or after etc. etc.
Of course, querying for individual segments in the audio file like
words or sounds is possible only, if this information has been added to
the database before.
Signal processing in
R
The first task is to extract the time-amplitude waveform
representation (oscillogram) for certain single sounds, e.g. some long
aː.
# query all "aː" phonetic items
sl = query(db, "MAU == aː")
# instead of all 7,000 vowels take only 6
sl = sl[100:105, ]
The data for the oscillogram is stored in the SSF track
dft, which is extracted by get_trackdata. The
result is another R data frame.
# get "dft" track data for these segments
a_vowels = get_trackdata(emuDBhandle = db,
seglist = sl,
ssffTrackName = "MEDIAFILE_SAMPLES",
verbose = TRUE)
The oscillograms for these 6 vowel instances can then be visualised
with ggplot2.
# plot oscillogram
ggplot(data = a_vowels) +
# define the df columns for the x and y values
aes(y = T1, x = times_rel) +
# line chart
geom_line() +
# sl_rowIdx groups all rows in a data frame belonging to the same segment
# labels contains the label of the segment
facet_wrap(~ sl_rowIdx + labels)
# query all words beginning with 'Fluch'
#
sl = query(db, query = "[ ORT =~ 'Fluch.*']")
# get "f0" track data for these segments, in this case calculated on the fly
fluch_words = get_trackdata(emuDBhandle = db,
seglist = sl,
# using emuR's Michel Scheffers’ Modified Harmonic Sieve algorithm
onTheFlyFunctionName = "mhsF0",
verbose = TRUE)
In the corresponding graphs then the track for the fundamental
frequency (f0) for some isolated words will be drawn.
# plot f0 tracks
ggplot(data = fluch_words) +
# define the df columns for the x and y values
# T1 here is the f0 value
aes(y = T1, x = times_rel) +
# line chart
geom_line() +
# sl_rowIdx groups all rows in a data frame belonging to the same segment
# labels contains the label of the segment
facet_wrap(~ sl_rowIdx + labels)
If needed, you can check the content of your segment list with the
EMU WebApp running serve(seglist = sl).
Calculate duration for
vowel categories
The study of duration of speech segments is a standard task in
acoustic phonetics. Let’s see how to solve this in R.
Thanks to the label groups which are available in the
database, we have easy access to e.g. all shortMonophthongs
and all longMonophthongs.
# query all long and short vowels
longMonophthongs = query(db, "MAU == longMonophthongs")
shortMonophthongs = query(db, "MAU == shortMonophthongs")
# bind the two together to one segment list sl
sl = rbind(longMonophthongs, shortMonophthongs)
# print the count; number of rows
nrow(longMonophthongs)
nrow(shortMonophthongs)
The data frame contains per segment a start and end column, which
allows for simple calculation of the segment duration.
# calculate durations
duration_long = longMonophthongs$end - longMonophthongs$start
duration_short = shortMonophthongs$end - shortMonophthongs$start
# calculate mean and standard deviation
paste("The mean duration for long monophthongs in the databse is", round(mean(duration_long)), "ms. The standard deviation is:", sd(duration_long), ".")
paste("The mean duration for short monophthongs in the databse is", round(mean(duration_short)), "ms. The standard deviation is:", sd(duration_short), ".")
Instead of having these broad categories long and
short we can break them down to the individual vowels. The
dataframes just created contain the individual vowels already in the
labels column and can thus be easily utilised.
head(longMonophthongs)
First, we create an aggregated dataframe with all vowel segments.
This aggregated dataframe now contains 127,065 rows (= long/short
vowels).
# add new columns for length, either long or short
longMonophthongs$length <- "long"
shortMonophthongs$length <- "short"
# then merge the two data frame into a single one
all_vowels <- rbind(longMonophthongs, shortMonophthongs)
# add a new colum for segment duration
all_vowels$duration <- round(all_vowels$end - all_vowels$start)
# todo: keep propper sl throughout, will be needed later for requeries!
sl <- all_vowels %>% filter(!str_detect(bundle, "^[CDDEF].*"))
sl$length <- NULL
sl$duration <- NULL
Using the powerful library dplyr, we can now calculate
the means for all vowels (= labels).
duration_vowels <- all_vowels %>%
group_by(labels, length) %>%
summarise(mean = mean(duration), sd = sd(duration))
duration_vowels
Finally, visualise the means in a bar chart
# basic setting for ggplot with data and aesthetics
ggplot(data = duration_vowels, aes(x= labels, y= mean, fill = length)) +
geom_bar(stat="identity", position=position_dodge()) +
# add errorbars
geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), width=.2,
position=position_dodge(.9)) +
theme_minimal()
From here on the duration values can be utilised in further
statistical analyses.
Vowels formants and
visualisations with ggplot2
Formant frequencies are the acoustic representation of the vowel’s
quality. Of these, the first two formants are crucial for the vowels
identity, i.e. F1 and F2. The values for the formants are already
calculated in the database and will now be extracted for all the vowels
our dataframe all_vowels.
As it takes long to do this, the dataframe has been precompiled and
saved to file … and reloaded again.
#saveRDS(td_vowels, file ="td_vowels.RDS")
# td_vowels <- readRDS("td_vowels.RDS")
Due to the varying length of all vowels, it is necessary to normalize
the length with normalize_length. This step takes long and,
again, the dataframe has already been created and saved.
# normalise the length
#td_vowels_norm = normalize_length(td_vowels %>% select(-duration, -length), colNames = c("T1", "T2"))
# do some cleaning
# exclude words spoken by another (male) speaker
# td_vowels_norm <- td_vowels_norm %>%
# filter(!str_detect(bundle, "^[CDDEF].*"))
# save this dataframe
#saveRDS(td_vowels_norm, file ="td_vowels_norm.RDS")
# load this data frame
td_vowels_norm <- readRDS("td_vowels_norm.RDS")
Formants are calculated for every sampling point of the audio file.
Thus, the dataframe td_vowels_norm now contains some 2,1
million rows/values.
For illustration purposes, the formants for the long vowels in three
example words are selected into a dataframe (maachen, Viz,
Kuuscht).
formant_examples <- td_vowels_norm %>%
filter(sl_rowIdx == "722" | sl_rowIdx == "21702" | sl_rowIdx == "11191")
These three vowels then are visualised as line charts.
ggplot(data=formant_examples) +
geom_smooth(aes(x = times_norm, y = T1, col = labels, group = labels)) +
geom_smooth(aes(x = times_norm, y = T2, col = labels, group = labels)) +
labs(x = "Duration (normalized)", y = "F1 + F2 (Hz)") +
ggtitle("Example formants F1, F2 the three corner vowels") +
facet_wrap(~ labels)
To get the identity of a vowel, it is common practice to select only
one point in the middle of the vowel. To do so, I am using a
filter and select the F1 and F2 values at the normalised
time point 0.4. This gives us F1 and F2 values for 104,055
vowels.
vowel_midpoints = td_vowels_norm %>%
filter(times_norm == 0.4)
vowel_midpoints$labels <- as.factor(vowel_midpoints$labels)
Again, some further data conversion.
# convert Hertz to Bark
# convert T1, T2, T3 to Bark
td_vowels_norm$T1 <- emuR::bark(td_vowels_norm$T1)
td_vowels_norm$T2 <- emuR::bark(td_vowels_norm$T2)
# insert new columns from requeries to have the info about the next and next_next and previous label after the vowel available in the dataframe
# vowel_midpoints$next_label = as.factor(requery_seq(db, seglist = sl, offset=1)$labels)
# vowel_midpoints$next_next_label = as.factor(requery_seq(db, seglist = sl, offset=2, ignoreOutOfBounds =TRUE)$labels)
# vowel_midpoints$word = requery_hier(db, seglist = sl, level="ORT")$labels
# vowel_midpoints$previous_label <- as.factor(requery_seq(db, seglist = sl, offset= -1)$labels)
#
# saveRDS(vowel_midpoints, "vowel_midpoints.RDS")
vowel_midpoints <- readRDS("vowel_midpoints.RDS")
The dataframe vowel_midpoints is now enriched with
additional columns for the actual word (of a vowel
instance), previous_label, next_label and
next_next_label, which allows to select data more
fine-grained for the following visualisations, e.g. all vowels
aː with a g in front and an R
after it.
head(vowel_midpoints)
Prepared in such a way, the visualisations of this sheer amount of
+100,000 vowels can commence. Vowel quality is charted in scatter plots,
where the F1 value is plotted on the y-axis and the F2 value on the
y-axis. To gain resemblance with the articulation of vowels, both axes
are reversed. Thus i is located in the top front,
u in the top back and a in the low
position.
The result looks rather flashy, but nevertheless has some flaws.
First, there are several outliers, especially in the SW corner. An
elimination procedure for outliers is needed. Secondly, the single
clouds for each vowels are overlapping each other to such an extent that
the proper location is not identifiable.
# takes too long: do not run with full dataset!
# ggplot(vowel_midpoints) +
# aes(x = T2, y = T1, label = labels, col = labels) +
# geom_text() +
# scale_y_reverse() +
# scale_x_reverse(breaks = seq(from=7, to=14, by=1), minor_breaks = NULL) +
# labs(x = "F2 (Hz)", y = "F1 (Hz)") +
# ggtitle("Scatter plot of all vowels in the LOD database")
# ggsave("plot_vowel_midpoints.png")
Next task: remove outliers. I am using a function based on the
(Mahalnobis distance)\[<https://en.wikipedia.org/wiki/Mahalanobis_distance>\],
which incrementally excludes outliers up to a predefined threshold. In
the present case we keep 85 % of the values.
without_outliers contains some 88,000 rows, meaning
nearly 20,000 have been identified and removed as outliers.
To reduce the amount of data points in the scatter plot, one can draw
contour plots (following this
idea). This method geom_density2d facilitates to
identify the core zones of vowel realisations, i.e. where - similar to a
weather chart - the lines are closer together.
ggplot(without_outliers, aes(x = T2, y = T1, color=labels)) +
geom_density2d(aes(label= labels)) +
scale_y_reverse() +
scale_x_reverse(breaks = seq(from=7, to=14, by=1), minor_breaks = NULL) +
labs(x = "F2 (Bark)", y = "F1 (Bark)") +
ggtitle("Contour plot of all vowels in the LOD database")
Warnung: Ignoring unknown aesthetics: label

Instead of displaying each and every data point, draw only an ellipse
for the extent of a vowel cloud. For each ellipse the middle point, the
so-called centroid is also computed.
# plot centroids and ellipses
centroid = without_outliers %>%
group_by(labels) %>%
summarize(F1=mean(T1), F2=mean(T2))
ggplot(without_outliers) +
aes(x=T2,y=T1, col=labels,label=labels)+
stat_ellipse() +
geom_text(data=centroid,aes(x=F2,y=F1)) +
scale_y_reverse() + scale_x_reverse() +
labs(x = "F2 (Hz)", y = "F1 (Hz)") +
theme(legend.position="none") +
ggtitle("Dispersion of vowels in Luxembourgish")

A further possibility is to use the function facet_wrap
which creates single plot for the factors of a variable. Here,
individual plots are displayed according to the labels ( = vowels) in
the dataframe. The elliptoid shape of each cloud is a result of the
outlier removal procedure.
ggplot(without_outliers %>% filter(!(labels =="ɐ" | labels == 'ə' | labels == 'ɔː'))) +
aes(x = T2, y = T1, label = labels, col = labels) +
geom_density2d(aes(label= labels)) +
#geom_text() +
scale_y_reverse() +
scale_x_reverse(breaks = seq(from=7, to=14, by=1), minor_breaks = NULL) +
labs(x = "F2 (Bark)", y = "F1 (Bark)") +
ggtitle("Scatter plot of all vowels in the LOD database") +
facet_wrap(~ labels)
Warnung: Ignoring unknown aesthetics: label

Sound change in
progress: The overlap of vowels
From the visualisation shown so far it becomes obvious that vowels
are not neatly separated but rather often show a certain amount of
overlap. This is either due to measurement errors or is indicating that
a vowel production is changing, i.e. that we are dealing with sound
change. Sound change usually manifests itself in subconscious, minimal
deviations from a former prototypical sound and these deviation may
become more prominent at a later stage.
In Luxembourgish, too, several sound changes are in progress for the
vowels. The last example in this tutorial covers the variational pattern
of the two e-vowels, \[e\] (in words
like Méck, bréngen, sécher) and \[ə\] (like in Schëff, Dësch).
While for elder speakers these vowels are distinct, there is an ongoing
merger for younger speaker by pronouncing the vowel before the certain
consonants like \[ə\] instead of \[e\].
To tackle this task, we first need the relevant data, which has been
prepared in a dataframe merger.
merger <- readRDS("e_merger_data.RDS")
Warnung in gzfile(file, "rb")
kann komprimierte Datei 'e_merger_data.RDS' nicht öffnen. Grund evtl. 'No such file or directory'
Fehler in gzfile(file, "rb") : kann Verbindung nicht öffnen
With around 2,100 rows this dataset is not too large for a scatter
plot. The relevant information is the horizontal overlap between the
clouds. While two of the red ones (\[k\], \[ŋ\]) are quite distinct from the green
one, it is the cloud for \[ɕ\] which
is showing overlap with the cloud for \[ʃ\]. Here we have clear evidence of an
ongoing vowel merger, i.e. the e-vowel realisations before \[ɕ\] and \[ʃ\] are becoming more similar.

The actual amount of overlap between two cloud-like data sets can be
calculated with the so-called Pillai score Details see here.
Basically, high Pillai scores indicate distinct clouds, low Pillai
score indicate merger. (Source
of figure)

Let’s now calculate the Pillai scores for the overlap. We extract the
rows for \[e\] before \[ɕ\] and \[ə\] before \[ʃ\], respectively, and merge these to a
temporary df ‘vowels’.
# select rows for the two sounds
e_ch_vowels <- merger %>%
filter((labels == "e" & next_label =="ɕ"))
e_sch_vowels <- merger %>%
filter((labels == "ə" & next_label =="ʃ"))
# new df with the selected rows
vowels <- rbind(e_ch_vowels, e_sch_vowels)
# calculate the Pillai score
pillai(cbind(T1, T2) ~ labels, data = vowels)
[1] 0.2905629
Remember, the Pillai score ranges between 0 and 1. The lower the
score, the closer two groups of realisations. With this rather low value
the Pillai score implies that the two vowel realisations are indeed
rather close and merging into each other.
Vowel explorer (Shiny
app)
Try it here.

LS0tCnRpdGxlOiAiQmlnIGRhdGEgaW4gdGhlIGFjb3VzdGljIHBob25ldGljIGFuYWx5c2lzIgphdXRob3I6ICJQZXRlciBHaWxsZXMiCmRhdGU6ICIyLiBBcHJpbCAyMDIyLCAxNGgwMCAtIDE1aDMwLCBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmciCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzInCiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKa25pdDogKGZ1bmN0aW9uKGlucHV0X2ZpbGUsIGVuY29kaW5nKSB7CiAgICBvdXRfZGlyIDwtICdkb2NzJzsKICAgIHJtYXJrZG93bjo6cmVuZGVyKGlucHV0X2ZpbGUsCiAgICAgIGVuY29kaW5nPWVuY29kaW5nLAogICAgICBvdXRwdXRfZmlsZT1maWxlLnBhdGgoZGlybmFtZShpbnB1dF9maWxlKSwgb3V0X2RpciwgJ2luZGV4Lmh0bWwnKSl9KQoKdXJsOiBodHRwczovL3R3aXR0ZXIuY29tL1BldGVyR2lsbGVzCnN1YnRpdGxlOiAnTGVjdHVyZSBmb3IgY2xhc3M6IERhdGEgc2NpZW5jZSBpbiB0aGUgSHVtYW5pdGllcycKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMgVG9waWNzIHsudW5udW1iZXJlZH0KClRoaXMgdHV0b3JpYWwgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG8gYW5hbHlzZSBhdWRpbyBkYXRhIGZvciBhY291c3RpYyBwaG9uZXRpYyBzdHVkaWVzIGluIFIuIEl0IGlzIG1haW5seSBpbnRlbmRlZCB0byBkZW1vbnN0cmF0ZWQgcG9zc2libGUgd29ya2Zsb3dzLiBUaGUgdG9waWNzIGNvdmVyZWQgYXJlOgoKLSAgIFBob25ldGljIGRhdGFiYXNlcywgdGhlIGNhc2Ugb2YgYGVtdVJgIGFuZCBgRU1VLVNETVNgLCB0aGUgRU1VIFNwZWVjaCBEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbQotICAgU2FtcGxlIGRhdGE6IHRoZSBMT0QgZGF0YWJhc2UKLSAgIFF1ZXJpZXMgYW5kIHJlcXVlcmllcwotICAgSW5zcGVjdCB0aGUgZGF0YWJhc2U6IGBzZXJ2ZSgpYAotICAgQ2FsY3VsYXRlIGR1cmF0aW9uIGZvciB2b3dlbCBjYXRlZ29yaWVzCi0gICBWb3dlbHMgZm9ybWFudHMgYW5kIHZpc3VhbGlzYXRpb25zIHdpdGggYGdncGxvdDJgCi0gICBDYWxjdWxhdGluZyB0aGUgUGlsbGFpIGRpc3RhbmNlCi0gICBWb3dlbCBleHBsb3JlcgoKIyBQcmVhbWJsZSB7LnVubnVtYmVyZWR9CgpUaGUgZ2VuZXJhdGVkIEhUTUwgcGFnZSBvZiB0aGlzIHR1dG9yaWFsIGlzIGF2YWlsYWJsZSBbaGVyZV0oaHR0cHM6Ly9wZXRlcmdpbGxlcy5naXRodWIuaW8vRGF0YV9TY2llbmNlX0h1bWFuaXRpZXMvaW5kZXgubmIuaHRtbCkuCgpUaGlzIHR1dG9yaWFsIGlzIG9yZ2FuaXNlZCBhcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgbm90ZWJvb2suIFRvIGV4ZWN1dGUgdGhlIGNvZGUgd2l0aGluIHRoaXMgbm90ZWJvb2sgKGZpbGVuYW1lIGBMZWN0dXJlLlJtZGApIGl0IGhhcyB0byBiZSBvcGVuZWQgaW4gUlN0dWRpby4gV2hlbiBleGVjdXRpbmcgY29kZSwgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIEluIG9yZGVyIHRvIGRvIHNvLCB5b3UgbmVlZCB0byBoYXZlIFIgYW5kIFJTdHVkaW8gaW5zdGFsbGVkLiBXaGVuIHRoaXMgUiBub3RlYm9vayBpcyBsb2FkZWQgaW50byBSU3R1ZGlvLCB5b3UgY2FuIGV4ZWN1dGUgY2h1bmtzIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkNtZCtTaGlmdCtFbnRlciosIGFsbG93aW5nIHlvdSB0byBleHBlcmltZW50IHdpdGggdGhlIGNvZGUuIFRoaXMgaGFuZG91dCBjb250YWlucyBhbGwgb3V0cHV0IG9mIHRoZSBjb2RlICh0YWJsZXMsIHZpc3VhbGlzYXRpb25zIGV0Yy4pLiBUaGUgZWFzaWVzdCB3YXkgdG8gd29yayB3aXRoIHRoaXMgUiBjb2RlIGlzIHRvIGNsb25lIHRoZSBlbnRpcmUgcHJvamVjdCBmcm9tIHRoaXMgR2l0SHViIHJlcG9zaXRvcnkuCgpUaGUgZm9sbG93aW5nIGxpYnJhcmllcyB3aWxsIGJlIG5lZWRlZCBhbmQgaGF2ZSB0byBiZSBpbnN0YWxsZWQuCgpgYGB7ciBMaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGVtdVIpCmxpYnJhcnkodG9vbHMpCmxpYnJhcnkocmlvKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoaHRtbHRvb2xzKQpsaWJyYXJ5KGpvZXlyKQpsaWJyYXJ5KGtuaXRyKQpgYGAKCiMgRm9yY2VkIGFsaWdubWVudAoKQmVmb3JlIHN0YXJ0aW5nIHRvIHdvcmsgd2l0aCB0aGUgZGF0YWJhc2UsIGxldCdzIGhhdmUgYSBxdWljayBvdmVydmlldyBob3cgdGhlIGF1dG9tYXRpYyBzZWdtZW50YXRpb24gb2YgYXVkaW8gZmlsZXMgaW50byBzbWFsbGVyIGNodW5rcyBsaWtlIHdvcmRzIGFuZCBwaG9uZXRpYyBzZWdtZW50cyBhY3R1YWxseSBpcyB3b3JraW5nLgoKV2UgYXJlIHVzaW5nIHRoZSBbTUFVUyB0b29sc10oaHR0cHM6Ly9jbGFyaW4ucGhvbmV0aWsudW5pLW11ZW5jaGVuLmRlL0JBU1dlYlNlcnZpY2VzL2ludGVyZmFjZSkgZnJvbSB0aGUgVW5pdmVyc2l0eSBvZiBNdW5pY2guIE5leHQgdG8gYSB3ZWIgaW50ZXJmYWNlLCBNQVVTIHNlZ21lbnRhdGlvbiBjYW4gYmUgY2FsbGVkIGRpcmVjdGx5IGZyb20gd2l0aGluIFIgdGhyb3VnaCBhbiBBUEkuCgpUaGUgaW5wdXQgaXMgYSBzdHJlYW0gb2YgYXVkaW8gcmVjb3JkaW5nIGFsb25nc2lkZSBvZiBhIHdyaXR0ZW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIHdvcmRzIHNwb2tlbiBpbiB0aGlzIHJlY29yZGluZy4gQSB0cmFpbmVkIG1vZGVsIG9mIHRoZSBsYW5ndWFnZSwgaGVyZTogTHV4ZW1ib3VyZ2lzaCwgdGhlbiBpcyB1c2VkIHRvIG1hcCB0aGUgc291bmRzIGFuZCB3b3JkcyBkZXJpdmVkIGZyb20gdGhlIHRleHQgdG8gdGhlIHNwZWVjaCBzaWduYWwuIFRoZSBvdXRwdXQgaXMgYW4gb2JqZWN0IHdpdGggdGVtcG9yYWwgYWxpZ25tZW50cyBiZXR3ZWVuIHdvcmRzIGFuZCBzb3VuZHMuIFRoaXMgaXMgYSB3ZWFrIGZvcm0gb2Ygc3BlZWNoIHJlY29nbml0aW9uLgoKVGV4dCBmb3IgYSB0ZXN0IG9mIGZvcmNlZCBhbGlnbm1lbnQ6ICpEZW4gYWxlIE1hbm4gYXNzIGFuIGRhdCBrYWFsdCBXYWFzc2VyIGdlZmFsbC4qIChUaGUgb2xkIG1hbiBmZWxsIGludG8gdGggY29sZCB3YXRlci4pIHdpdGggdGhlIGNvcnJlc3BvbmRpbmcgc291bmQgZmlsZSBzYXZlIHRvZ2V0aGVyIGluIHRoZSBzYW1lIGRpcmVjdG9yeS4KCmBgYHtyIGV2YWw9RkFMU0V9CiMgY3JlYXRlIGFuIGVtdURCIGZyb20gdGhpcyB0ZXh0ICsgYXVkaW8KY29udmVydF90eHRDb2xsZWN0aW9uKGRiTmFtZSA9ICJ0ZXN0RGIiLAogICAgICAgICAgICAgICAgICAgICAgc291cmNlRGlyID0gIi4vdGVzdERCIiwKICAgICAgICAgICAgICAgICAgICAgIHRhcmdldERpciA9ICIuIikKYGBgCgpgYGB7cn0KIyBsb2FkIHRoaXMgZGIKZGIgPC0gbG9hZF9lbXVEQigidGVzdERiX2VtdURCIikKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQojIHJ1biBmb3JjZWQgYWxpZ21lbnQgdXNpbmcgQkFTIE1BVVMgdG9vbHMgaW4gUgpydW5CQVN3ZWJzZXJ2aWNlX2FsbChkYiwgCiAgICAgICAgICAgICAgICAgICAgIHRyYW5zY3JpcHRpb25BdHRyaWJ1dGVEZWZpbml0aW9uTmFtZSA9ICJ0cmFuc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgbGFuZ3VhZ2UgPSJsdHotTFUiLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgcnVuTUlOTkkgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgcmVzdW1lID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgIHBhdGllbmNlID0gMykKYGBgCgpTaG93IGEgc3VtbWFyeSBvZiB0aGUgZGF0YWJhc2UuCgpgYGB7cn0KIyBkaXNwbGF5IHRoZSBvdmVydmlldyBvZiB0aGUgc3RydWN0dXJlIGFuZCBjb250ZW50CnN1bW1hcnkoZGIpCmBgYAoKVGhlIHJlc3VsdCBvZiB0aGUgcXVlcnkgY2FuIGFsc28gYmUgZGlzcGxheWVkIGluIHRoZSBFTVUgU3BlZWNoIERhdGFiYXNlIE1hbmFnZW1lbnQgU3lzdGVtLgoKYGBge3IgZXZhbD1GQUxTRX0KIyBvcGVuIHRoZSBkYXRhYmFzZSBpbiBhIHdlYnZpZXdlcgpzZXJ2ZShkYikKYGBgCgojIFRoZSBMT0QgZGF0YWJhc2UKClRoZSBkYXRhYmFzZSBjb250YWlucyB0aGUgYXVkaW8gcmVjb3JkaW5ncyBmcm9tIHRoZSBgTMOrdHplYnVlcmdlciBPbmxpbmUgRGljdGlvbm5haXJlYCAoYXZhaWxhYmxlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vc3BlbGxjaGVja2VyLWx1KSwgc3Bva2VuIGJ5IG9uZSBmZW1hbGUgc3BlYWtlci4gVGhlIGF1ZGlvIGZpbGVzIGhhdmUgYmVlbiBhdXRvbWF0aWNhbGx5IHNlZ21lbnRlZCB3aXRoIHRoZSBbTUFVUyB0b29sc10oaHR0cHM6Ly9jbGFyaW4ucGhvbmV0aWsudW5pLW11ZW5jaGVuLmRlL0JBU1dlYlNlcnZpY2VzL2ludGVyZmFjZSkuIFdlIHRodXMgaGF2ZSBhIGRhdGFiYXNlIGNvbmlzdGluZyBvZiB0ZXh0dWFsIGRhdGEsIGJhc2ljYWxseSB3b3JkcywgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGF1ZGlvIGRhdGEuIFRoZSBhdWRpbyBkYXRhIGlzIHNlZ21lbnRlZCBpbnRvIHdvcmRzIGFuZCBwaG9uZXRpYyBzZWdtZW50cyAoc291bmRzKS4KClRoaXMgZGF0YWJhc2UgaGFzIGJlZW4gY3JlYXRlZCBiZWZvcmVoYW5kLiBJbmZvcyBob3cgdG8gY3JlYXRlIHN1Y2ggYSBkYXRhYmFzZSBpcyBleHBsYWluZWQgaW4gdGhlIFtFTVUtU0RNUyBtYW51YWxdKGh0dHBzOi8vaXBzLWxtdS5naXRodWIuaW8vVGhlLUVNVS1TRE1TLU1hbnVhbC8pLgoKV2hpbGUgd2Ugd2lsbCBiZSB3b3JraW5nIGluIHRoaXMgdHV0b3JpYWwgd2l0aCB0aGUgZnVsbCBkYXRhYmFzZSAoMjYuMDAwIHJlY29yZGluZ3MpLCBhIHNtYWxsIGRlbW8gZGF0YWJhc2UgYGxvZF9lbXVEQmAgaGFzIGJlZW4gY3JlYXRlZCB3aXRoIDUwMCByZWNvcmRpbmdzIHdoaWNoIGNhbiBiZSB1c2VkIGZvciBpbmRpdmlkdWFsIHRlc3RpbmcuCgojIExvYWRpbmcgYSBkYXRhYmFzZQoKV2Ugc3RhcnQgd2l0aCBsb2FkaW5nIHRoZSBkYXRhYmFzZSBhbmQgZ2l2ZSBhbiBvdmVydmlldyBvZiBzdHJ1Y3R1cmUgYW5kIGNvbnRlbnQuCgpgYGB7cn0KIyBsb2FkIGRhdGFiYXNlCmRiID0gbG9hZF9lbXVEQigiL1VzZXJzL3BldGVyLmdpbGxlcy9Eb2N1bWVudHMvX0RhdGVuL0xPRC1lbXVEQi9sb2RfZW11REIiKQojIGZvciB0ZXN0aW5nIHB1cnBvc2VzLCB1c2UgdGhpcyBzbWFsbGVyIGRhdGFiYXNlIGluIHRoZSBHaXRodWIgZm9sZGVyCiNkYiA9IGxvYWRfZW11REIoImxvZF9lbXVEQiIpCgojIGRpc3BsYXkgdGhlIG92ZXJ2aWV3IG9mIHRoZSBzdHJ1Y3R1cmUgYW5kIGNvbnRlbnQKc3VtbWFyeShkYikKCmBgYAoKVHJhY2tzIGluIGBlbXVSYCBhcmUgYWNvdXN0aWMgcmVwcmVzZW50YXRpb24gb2YgdGhlIHNwZWVjaCBzaWduYWwsIGhlcmUgYGRmdGAgZm9yIHRoZSB3YXZlZm9ybSAodGltZS1hbXBsaXR1ZGUgcmVwcmVzZW50YXRpb24pIGFuZCBgcHJhYXRGbXNgIGZvciB0aGUgZm9ybWFudCBtZWFzdXJlcyBvZiB2b3dlbHMgKHNlZSBiZWxvdykuCgpMZXZlbHMgaW4gYW4gYGVtdVJgIGRhdGFiYXNlIHN0YW5kIGZvciBsZXZlbCBvZiBpbnRlcmxpbmtlZCBsaW5ndWlzdGljIGluZm9ybWF0aW9uLiBgYnVuZGxlYCBpcyB0aGUgZW50aXJlIGF1ZGlvIGZpbGUsIGBPUlRgIHN0YW5kcyBmb3IgdGhlIG9ydGhvZ3JhcGhpY2FsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBhdWRpbyBmaWxlIHNlZ21lbnRlZCBpbiBpdHMgc2luZ2xlIHdvcmRzLiBgTUFVYCBpcyB0aGUgc2VnbWVudGF0aW9uIG9mIGFsbCBwaG9uZXRpYyBzZWdtZW50ICg9c291bmRzKSBvZiBhbGwgYE9SVGAgc2VnbWVudHMgaW4gYGJ1bmRsZWAuCgpUaGUgaGllcmFyY2hpY2FsIHN0cnVjdHVyZSBvZiB0aGVzZSBsZXZlbHMgaXMgZXhwcmVzc2VkIGluIHRoZSBgbGluayBkZWZpbml0aW9uc2AgYXMgYE9ORS1UTy1NQU5ZYC4KCiMgRGF0YWJhc2UgcXVlcmllcwoKQW4gZW11UiBkYXRhYmFzZSBjYW4gYmUgcXVlcmllZCB3aXRoIGEgcG93ZXJmdWwgcXVlcnkgZW5naW5lLiBUaGUgZmlyc3QgZXhhbXBsZSBpcyBhIHNpbXBsZSBxdWVyeSBmb3Igb25lIHdvcmQsIGBBYXJiZWNodGAuCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyBPUlQgPT0gJ0FhcmJlY2h0J10iKQpzbApgYGAKClRoZSByZXN1bHQgaXMgYSBgc2VnbWVudCBsaXN0YCAoYHNsYCksIGNvbnRhaW5pbmcgdmFyaW91cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZm91bmQgaXRlbSAodGltZSwgbGV2ZWwsIG5hbWUsIGRhdGFiYXNlIGluZm8pLiBUaGUgcmVzdWx0IG9mIHRoZSBxdWVyeSBjYW4gYWxzbyBiZSBkaXNwbGF5ZWQgaW4gdGhlIEVNVSBTcGVlY2ggRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW0uCgpgYGB7ciBldmFsPUZBTFNFfQpzZXJ2ZShkYiwgc2VnbGlzdCA9IHNsKQpgYGAKClRoZSBHVUkgd2lsbCBvcGVuIGluIHRoZSBgVmlld2VyYCBwYW5lIG9mIFJTdHVkaW8gb3IgeW91IGNhbiBvcGVuIGl0IGluIGEgYnJvd3NlciAoQ2hyb21lIHByZWZlcnJlZCkuCgpgYGB7ciBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhyZXAoImVtdS1zZG1zLnBuZyIpKQpgYGAKCkhlcmUgd2UgY2FuIGFsc28gZGlzcGxheSB0aGUgaGllcmFyY2hpY2FsIHN0cnVjdHVyZSBmb3IgdGhpcyBkYXRhYmFzZSBpdGVtLCB3aGljaCBpcyBhY2Nlc3NlZCBkdXJpbmcgcXVlcmllcy4gYGJ1bmRsZWAgaXMgdGhlIHRvcC1sZXZlbCwgcmVwcmVzZW50aW5nIHRoZSBlbnRpcmUgYXVkaW8gZmlsZS4KClRoZSBgT1JUYCBsZXZlbCBjb250YWlucyB0aGUgbm9kZXMgZm9yIHRoZSBpbmRpdmlkdWFsIHdvcmRzIGluIHRoZSBgYnVuZGxlYCwgaGVyZSB0aGUgdHdvIHdvcmRzIGBBYXJiZWNodGAgYW5kIGBBYXJiZWNodGVuYC4gVGhlIGRlcGVuZGVudCBsZXZlbCB0aGVuIGlzIGBNQVVgICg9YE11bmljaCBBdXRvbWF0aWMgVW5pdGApIHJlcHJlc2VudGluZyB0aGUgc2luZ2xlIHNvdW5kcyBvZiB0aGUgd29yZHMgaW4gYE9SVGAuCgpgYGB7ciBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhyZXAoImhpZXJhcmNoeS5wbmciKSkKYGBgCgpUd28gYXNwZWN0cyByZW5kZXIgZW11UiBxdWVyeSBzeXN0ZW0gZXh0cmVtZWx5IHBvd2VyZnVsOiB0aGUgdXNlIG9mIHJlZ3VsYXIgZXhwcmVzc2lvbnMgKGluY2x1ZGluZyBuZWdhdGlvbiBhbmQgb3RoZXIgZXh0ZW5zaW9ucykgYW5kIHRoZSBjb21iaW5lZCBxdWVyeSBvbiBkaWZmZXJlbnQgbGV2ZWxzIG9mIHRoZSBkYXRhYmFzZS4KCkxldCdzIHRyeSBtb3JlIGNvbXBsZXggcXVlcmllczoKClJlZ3VsYXIgZXhwcmVzc2lvbiwgb3BlcmF0b3IgYD1+YCwgd29yZHMgYmVnaW5uaW5nIHdpdGggYEFhcmJlY2h0Li4uYAoKYGBge3J9CnNsID0gcXVlcnkoZGIsIHF1ZXJ5ID0gIlsgT1JUID1+ICdBYXJiZWNodC4qJ10iKQpzbApgYGAKClF1ZXJ5OiBTZWxlY3QgdGhlIHZvd2VsIFxbYcuQXF0gaW4gYWxsIHdvcmRzIGJlZ2lubmluZyB3aXRoIGBBYXJiZWNodGAuLi4gTm90ZSB0aGF0IGluIHRoZSBzZWdtZW50IGxpc3QgdGhlIGxhYmVsIG5vdyBoYXMgY2hhbmdlZCB0byB0aGUgdm93ZWwgYW5kIHRoZSByZXNwZWN0aXZlIHN0YXJ0LWVuZCBpbmZvcm1hdGlvbiBpcyBub3cgb25seSBmb3IgdGhpcyBzb3VuZCBcW2HLkFxdLgoKYGBge3J9CnNsID0gcXVlcnkoZGIsIHF1ZXJ5ID0gIlsgT1JUID1+ICdBYXJiZWNodC4qJyBeICNNQVU9PSdhy5AnXSIpCnNsCgpgYGAKClF1ZXJ5IGEgc2VxdWVuY2Ugb2Ygc291bmRzLCBlLmcuIGBlYCBmb2xsb3dlZCBieSBga2AgKGBNw6lja2ApLCBieSB1c2luZyB0aGUgc2VxdWVuY2Ugb3BlcmF0b3IgYC0+YC4KCmBgYHtyfQpzbCA9IHF1ZXJ5KGRiLCBxdWVyeSA9ICJbIE1BVSA9PSBlIC0+IE1BVSA9PSBrIF0iKQojIHByaW50IG9ubHkgdGhlIGZpcnN0IDEwMCByb3dzCnNsWzE6MTAwLCBdCgpgYGAKCkluIHRoZSBzZXF1ZW5jZSBgZS0+a2AsIHF1ZXJ5IG9ubHkgdGhlIHZvd2VsLiBVc2UgdGhlIHJlc3VsdCBtb2RpZmllciBgI2AuCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyAjTUFVID09IGUgLT4gTUFVID09IGsgXSIpCiMgcHJpbnQgb25seSB0aGUgZmlyc3QgMTAwIHJvd3MKc2xbMToxMDAsIF0KYGBgCgpXdWVyeSBhbGwgc291bmQgaXRlbXMgdGhhdCBvY2N1ciBhdCB0aGUgZW5kIG9mIGEgd29yZCBhbmQgYXJlIFxbcFxdLgoKYGBge3J9CnNsIDwtIHF1ZXJ5KGRiLCBxdWVyeSA9ICJbRW5kKE9SVCwgTUFVKSA9PSBUUlVFICYgTUFVID09IHAgXSIpCnNsCmBgYAoKUmV0cmlldmUgYWxsIHdvcmQgdGhhdCBjb250YWluIGZpdmUgc2VnbWVudHMuCgpgYGB7cn0Kc2wgPC0gcXVlcnkoZGIsICJbTnVtKE9SVCwgTUFVKSA9PSA1XSIpCnNsCmBgYAoKVXNpbmcgYHJlcXVlcnlgLCB0aGUgcmVzdWx0cyBmcm9tIGEgcHJldmlvdXMgcXVlcnkgY2FuIGJlIGZ1cnRoZXIgc3BlY2lmaWVkLgoKYGBge3J9CiMgcmVxdWVyeV9zZXEoKQoKIyBxdWVyeSBhbGwgIm0iIHBob25ldGljIGl0ZW1zCnNsX20gPSBxdWVyeShkYiwgIk1BVSA9PSBtIikKCiMgc2VxdWVudGlhbCByZXF1ZXJ5IChsZWZ0IHNoaWZ0IHJlc3VsdCBieSAxICg9PSBvZmZzZXQgb2YgLTEpKQojIGFuZCBoZW5jZSByZXRyaWV2ZSBhbGwgcGhvbmV0aWMgaXRlbXMgZGlyZWN0bHkgcHJlY2VlZGluZwojIGFsbCAibSIgcGhvbmV0aWMgaXRlbXMKc2xfcmVxX24gPC0gcmVxdWVyeV9zZXEoZGIsIAogICAgICAgICAgICBzZWdsaXN0ID0gc2xfbSwgCiAgICAgICAgICAgIG9mZnNldCA9IC0xKQpzbF9yZXFfbgpgYGAKCkdyb3VwcyBvZiBzb3VuZHMgY2FuIGJlIGdyb3VwZWQgdG9nZXRoZXIgdG8gYGxhYmVsIGdyb3Vwc2A6IC0gYWxsIGxvbmcgbW9ub3BodGhvbmdzOiBgImnLkCIsICJ1y5AiLCAiYcuQIiwgIm/LkCIsICLJlMuQIiwgIsmby5AiLCAiZcuQImAgLSBhbGwgc2hvcnQgbW9ub3BodGhvbmdzOiBgImkiLCAidSIsICLJkSIsICJvIiwgIsOmIiwgImUiLCAiyZkiLCAiyZAiYAoKYGBge3J9CiMgcXVlcnkgYWxsIGxvbmcgbW9ub3BodGhvbmdzCnNsID0gcXVlcnkoZGIsICJNQVUgPT0gbG9uZ01vbm9waHRob25ncyIpCnNsCmBgYAoKV2l0aCB0aGUgcXVlcnkgdGhlIHVzZXIgY2FuIGNvbXBpbGUgYSBkYXRhIGZyYW1lIGZyb20gdGhlIGRhdGFiYXNlIHdoaWNoIHRoZW4gZm9ybXMgdGhlIHN1YnNldCBmb3IgZnVydGhlciBwaG9uZXRpYyBhbmFseXNpcy4gV2UgY2FuIHNlbGVjdCBlLmcuIGFsbCBpbnN0YW5jZXMgb2YgY2VydGFpbiAob3IgYWxsKSB2b3dlbHMsIHNwZWNpZnlpbmcgdGhlIGNvbnRleHQgYmVmb3JlIG9yIGFmdGVyIGV0Yy4gZXRjLgoKT2YgY291cnNlLCBxdWVyeWluZyBmb3IgaW5kaXZpZHVhbCBzZWdtZW50cyBpbiB0aGUgYXVkaW8gZmlsZSBsaWtlIHdvcmRzIG9yIHNvdW5kcyBpcyBwb3NzaWJsZSBvbmx5LCBpZiB0aGlzIGluZm9ybWF0aW9uIGhhcyBiZWVuIGFkZGVkIHRvIHRoZSBkYXRhYmFzZSBiZWZvcmUuCgojIFNpZ25hbCBwcm9jZXNzaW5nIGluIFIKClRoZSBmaXJzdCB0YXNrIGlzIHRvIGV4dHJhY3QgdGhlIHRpbWUtYW1wbGl0dWRlIHdhdmVmb3JtIHJlcHJlc2VudGF0aW9uIChvc2NpbGxvZ3JhbSkgZm9yIGNlcnRhaW4gc2luZ2xlIHNvdW5kcywgZS5nLiBzb21lIGxvbmcgYGHLkGAuCgpgYGB7cn0KIyBxdWVyeSBhbGwgImHLkCIgcGhvbmV0aWMgaXRlbXMKCnNsID0gcXVlcnkoZGIsICJNQVUgPT0gYcuQIikKIyBpbnN0ZWFkIG9mIGFsbCA3LDAwMCB2b3dlbHMgdGFrZSBvbmx5IDYKc2wgPSBzbFsxMDA6MTA1LCBdCmBgYAoKVGhlIGRhdGEgZm9yIHRoZSBvc2NpbGxvZ3JhbSBpcyBzdG9yZWQgaW4gdGhlIFNTRiB0cmFjayBgZGZ0YCwgd2hpY2ggaXMgZXh0cmFjdGVkIGJ5IGBnZXRfdHJhY2tkYXRhYC4gVGhlIHJlc3VsdCBpcyBhbm90aGVyIFIgZGF0YSBmcmFtZS4KCmBgYHtyfQojIGdldCAiZGZ0IiB0cmFjayBkYXRhIGZvciB0aGVzZSBzZWdtZW50cwphX3Zvd2VscyA9IGdldF90cmFja2RhdGEoZW11REJoYW5kbGUgPSBkYiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ2xpc3QgPSBzbCwKICAgICAgICAgICAgICAgICAgICAgICAgIHNzZmZUcmFja05hbWUgPSAiTUVESUFGSUxFX1NBTVBMRVMiLAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCmBgYAoKVGhlIG9zY2lsbG9ncmFtcyBmb3IgdGhlc2UgNiB2b3dlbCBpbnN0YW5jZXMgY2FuIHRoZW4gYmUgdmlzdWFsaXNlZCB3aXRoIGBnZ3Bsb3QyYC4KCmBgYHtyfQojIHBsb3Qgb3NjaWxsb2dyYW0KZ2dwbG90KGRhdGEgPSBhX3Zvd2VscykgKyAKICAjIGRlZmluZSB0aGUgZGYgY29sdW1ucyBmb3IgdGhlIHggYW5kIHkgdmFsdWVzCiAgYWVzKHkgPSBUMSwgeCA9IHRpbWVzX3JlbCkgKyAKICAjIGxpbmUgY2hhcnQKICBnZW9tX2xpbmUoKSArIAogICMgc2xfcm93SWR4IGdyb3VwcyBhbGwgcm93cyBpbiBhIGRhdGEgZnJhbWUgYmVsb25naW5nIHRvIHRoZSBzYW1lIHNlZ21lbnQKICAjIGxhYmVscyBjb250YWlucyB0aGUgbGFiZWwgb2YgdGhlIHNlZ21lbnQKICBmYWNldF93cmFwKH4gc2xfcm93SWR4ICsgbGFiZWxzKQpgYGAKCmBgYHtyfQojIHF1ZXJ5IGFsbCB3b3JkcyBiZWdpbm5pbmcgd2l0aCAnRmx1Y2gnCiMgCnNsID0gcXVlcnkoZGIsIHF1ZXJ5ID0gIlsgT1JUID1+ICdGbHVjaC4qJ10iKQoKIyBnZXQgImYwIiB0cmFjayBkYXRhIGZvciB0aGVzZSBzZWdtZW50cywgaW4gdGhpcyBjYXNlIGNhbGN1bGF0ZWQgb24gdGhlIGZseQpmbHVjaF93b3JkcyA9IGdldF90cmFja2RhdGEoZW11REJoYW5kbGUgPSBkYiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ2xpc3QgPSBzbCwKICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNpbmcgZW11UidzIE1pY2hlbCBTY2hlZmZlcnPigJkgTW9kaWZpZWQgSGFybW9uaWMgU2lldmUgYWxnb3JpdGhtIAogICAgICAgICAgICAgICAgICAgICAgICAgb25UaGVGbHlGdW5jdGlvbk5hbWUgPSAibWhzRjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCmBgYAoKSW4gdGhlIGNvcnJlc3BvbmRpbmcgZ3JhcGhzIHRoZW4gdGhlIHRyYWNrIGZvciB0aGUgZnVuZGFtZW50YWwgZnJlcXVlbmN5IChmMCkgZm9yIHNvbWUgaXNvbGF0ZWQgd29yZHMgd2lsbCBiZSBkcmF3bi4KCmBgYHtyfQojIHBsb3QgZjAgdHJhY2tzCmdncGxvdChkYXRhID0gZmx1Y2hfd29yZHMpICsgCiAgIyBkZWZpbmUgdGhlIGRmIGNvbHVtbnMgZm9yIHRoZSB4IGFuZCB5IHZhbHVlcwogICMgVDEgaGVyZSBpcyB0aGUgZjAgdmFsdWUKICBhZXMoeSA9IFQxLCB4ID0gdGltZXNfcmVsKSArIAogICMgbGluZSBjaGFydAogIGdlb21fbGluZSgpICsgCiAgIyBzbF9yb3dJZHggZ3JvdXBzIGFsbCByb3dzIGluIGEgZGF0YSBmcmFtZSBiZWxvbmdpbmcgdG8gdGhlIHNhbWUgc2VnbWVudAogICMgbGFiZWxzIGNvbnRhaW5zIHRoZSBsYWJlbCBvZiB0aGUgc2VnbWVudAogIGZhY2V0X3dyYXAofiBzbF9yb3dJZHggKyBsYWJlbHMpCmBgYAoKSWYgbmVlZGVkLCB5b3UgY2FuIGNoZWNrIHRoZSBjb250ZW50IG9mIHlvdXIgc2VnbWVudCBsaXN0IHdpdGggdGhlIEVNVSBXZWJBcHAgcnVubmluZyBgc2VydmUoc2VnbGlzdCA9IHNsKWAuCgojIENhbGN1bGF0ZSBkdXJhdGlvbiBmb3Igdm93ZWwgY2F0ZWdvcmllcwoKVGhlIHN0dWR5IG9mIGR1cmF0aW9uIG9mIHNwZWVjaCBzZWdtZW50cyBpcyBhIHN0YW5kYXJkIHRhc2sgaW4gYWNvdXN0aWMgcGhvbmV0aWNzLiBMZXQncyBzZWUgaG93IHRvIHNvbHZlIHRoaXMgaW4gYFJgLiBUaGFua3MgdG8gdGhlIGBsYWJlbCBncm91cHNgIHdoaWNoIGFyZSBhdmFpbGFibGUgaW4gdGhlIGRhdGFiYXNlLCB3ZSBoYXZlIGVhc3kgYWNjZXNzIHRvIGUuZy4gYWxsIGBzaG9ydE1vbm9waHRob25nc2AgYW5kIGFsbCBgbG9uZ01vbm9waHRob25nc2AuCgpgYGB7cn0KIyBxdWVyeSBhbGwgbG9uZyBhbmQgc2hvcnQgdm93ZWxzCmxvbmdNb25vcGh0aG9uZ3MgPSBxdWVyeShkYiwgIk1BVSA9PSBsb25nTW9ub3BodGhvbmdzIikKc2hvcnRNb25vcGh0aG9uZ3MgPSBxdWVyeShkYiwgIk1BVSA9PSBzaG9ydE1vbm9waHRob25ncyIpCgojIGJpbmQgdGhlIHR3byB0b2dldGhlciB0byBvbmUgc2VnbWVudCBsaXN0IHNsCnNsID0gcmJpbmQobG9uZ01vbm9waHRob25ncywgc2hvcnRNb25vcGh0aG9uZ3MpCgojIHByaW50IHRoZSBjb3VudDsgbnVtYmVyIG9mIHJvd3MKbnJvdyhsb25nTW9ub3BodGhvbmdzKQpucm93KHNob3J0TW9ub3BodGhvbmdzKQpgYGAKClRoZSBkYXRhIGZyYW1lIGNvbnRhaW5zIHBlciBzZWdtZW50IGEgc3RhcnQgYW5kIGVuZCBjb2x1bW4sIHdoaWNoIGFsbG93cyBmb3Igc2ltcGxlIGNhbGN1bGF0aW9uIG9mIHRoZSBzZWdtZW50IGR1cmF0aW9uLgoKYGBge3J9CiMgY2FsY3VsYXRlIGR1cmF0aW9ucwpkdXJhdGlvbl9sb25nID0gbG9uZ01vbm9waHRob25ncyRlbmQgLSBsb25nTW9ub3BodGhvbmdzJHN0YXJ0CmR1cmF0aW9uX3Nob3J0ID0gc2hvcnRNb25vcGh0aG9uZ3MkZW5kIC0gc2hvcnRNb25vcGh0aG9uZ3Mkc3RhcnQKCiMgY2FsY3VsYXRlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgpwYXN0ZSgiVGhlIG1lYW4gZHVyYXRpb24gZm9yIGxvbmcgbW9ub3BodGhvbmdzIGluIHRoZSBkYXRhYnNlIGlzIiwgcm91bmQobWVhbihkdXJhdGlvbl9sb25nKSksICJtcy4gVGhlIHN0YW5kYXJkIGRldmlhdGlvbiBpczoiLCBzZChkdXJhdGlvbl9sb25nKSwgIi4iKQoKcGFzdGUoIlRoZSBtZWFuIGR1cmF0aW9uIGZvciBzaG9ydCBtb25vcGh0aG9uZ3MgaW4gdGhlIGRhdGFic2UgaXMiLCByb3VuZChtZWFuKGR1cmF0aW9uX3Nob3J0KSksICJtcy4gVGhlIHN0YW5kYXJkIGRldmlhdGlvbiBpczoiLCBzZChkdXJhdGlvbl9zaG9ydCksICIuIikKYGBgCgpJbnN0ZWFkIG9mIGhhdmluZyB0aGVzZSBicm9hZCBjYXRlZ29yaWVzIGBsb25nYCBhbmQgYHNob3J0YCB3ZSBjYW4gYnJlYWsgdGhlbSBkb3duIHRvIHRoZSBpbmRpdmlkdWFsIHZvd2Vscy4gVGhlIGRhdGFmcmFtZXMganVzdCBjcmVhdGVkIGNvbnRhaW4gdGhlIGluZGl2aWR1YWwgdm93ZWxzIGFscmVhZHkgaW4gdGhlIGBsYWJlbHNgIGNvbHVtbiBhbmQgY2FuIHRodXMgYmUgZWFzaWx5IHV0aWxpc2VkLgoKYGBge3J9CmhlYWQobG9uZ01vbm9waHRob25ncykKYGBgCgpGaXJzdCwgd2UgY3JlYXRlIGFuIGFnZ3JlZ2F0ZWQgZGF0YWZyYW1lIHdpdGggYWxsIHZvd2VsIHNlZ21lbnRzLiBUaGlzIGFnZ3JlZ2F0ZWQgZGF0YWZyYW1lIG5vdyBjb250YWlucyAxMjcsMDY1IHJvd3MgKD0gbG9uZy9zaG9ydCB2b3dlbHMpLgoKYGBge3J9CiMgYWRkIG5ldyBjb2x1bW5zIGZvciBsZW5ndGgsIGVpdGhlciBsb25nIG9yIHNob3J0CmxvbmdNb25vcGh0aG9uZ3MkbGVuZ3RoIDwtICJsb25nIgpzaG9ydE1vbm9waHRob25ncyRsZW5ndGggPC0gInNob3J0IgoKIyB0aGVuIG1lcmdlIHRoZSB0d28gZGF0YSBmcmFtZSBpbnRvIGEgc2luZ2xlIG9uZQphbGxfdm93ZWxzIDwtIHJiaW5kKGxvbmdNb25vcGh0aG9uZ3MsIHNob3J0TW9ub3BodGhvbmdzKQoKIyBhZGQgYSBuZXcgY29sdW0gZm9yIHNlZ21lbnQgZHVyYXRpb24KYWxsX3Zvd2VscyRkdXJhdGlvbiA8LSByb3VuZChhbGxfdm93ZWxzJGVuZCAtIGFsbF92b3dlbHMkc3RhcnQpCgojIHRvZG86IGtlZXAgcHJvcHBlciBzbCB0aHJvdWdob3V0LCB3aWxsIGJlIG5lZWRlZCBsYXRlciBmb3IgcmVxdWVyaWVzIQpzbCA8LSBhbGxfdm93ZWxzICU+JSBmaWx0ZXIoIXN0cl9kZXRlY3QoYnVuZGxlLCAiXltDRERFRl0uKiIpKQpzbCRsZW5ndGggPC0gTlVMTApzbCRkdXJhdGlvbiA8LSBOVUxMCmBgYAoKVXNpbmcgdGhlIHBvd2VyZnVsIGxpYnJhcnkgYGRwbHlyYCwgd2UgY2FuIG5vdyBjYWxjdWxhdGUgdGhlIG1lYW5zIGZvciBhbGwgdm93ZWxzICg9IGBsYWJlbHNgKS4KCmBgYHtyfQpkdXJhdGlvbl92b3dlbHMgPC0gYWxsX3Zvd2VscyAlPiUKICBncm91cF9ieShsYWJlbHMsIGxlbmd0aCkgJT4lCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGR1cmF0aW9uKSwgc2QgPSBzZChkdXJhdGlvbikpCgpkdXJhdGlvbl92b3dlbHMKYGBgCgpGaW5hbGx5LCB2aXN1YWxpc2UgdGhlIG1lYW5zIGluIGEgYmFyIGNoYXJ0CgpgYGB7cn0KIyBiYXNpYyBzZXR0aW5nIGZvciBnZ3Bsb3Qgd2l0aCBkYXRhIGFuZCBhZXN0aGV0aWNzCmdncGxvdChkYXRhID0gZHVyYXRpb25fdm93ZWxzLCBhZXMoeD0gbGFiZWxzLCB5PSBtZWFuLCBmaWxsID0gbGVuZ3RoKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkgKwogICMgYWRkIGVycm9yYmFycwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bWVhbi1zZCwgeW1heD1tZWFuK3NkKSwgd2lkdGg9LjIsCiAgICAgICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoLjkpKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKRnJvbSBoZXJlIG9uIHRoZSBkdXJhdGlvbiB2YWx1ZXMgY2FuIGJlIHV0aWxpc2VkIGluIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzZXMuCgojIFZvd2VscyBmb3JtYW50cyBhbmQgdmlzdWFsaXNhdGlvbnMgd2l0aCBgZ2dwbG90MmAKCkZvcm1hbnQgZnJlcXVlbmNpZXMgYXJlIHRoZSBhY291c3RpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUgdm93ZWwncyBxdWFsaXR5LiBPZiB0aGVzZSwgdGhlIGZpcnN0IHR3byBmb3JtYW50cyBhcmUgY3J1Y2lhbCBmb3IgdGhlIHZvd2VscyBpZGVudGl0eSwgaS5lLiBGMSBhbmQgRjIuIFRoZSB2YWx1ZXMgZm9yIHRoZSBmb3JtYW50cyBhcmUgYWxyZWFkeSBjYWxjdWxhdGVkIGluIHRoZSBkYXRhYmFzZSBhbmQgd2lsbCBub3cgYmUgZXh0cmFjdGVkIGZvciBhbGwgdGhlIHZvd2VscyBvdXIgZGF0YWZyYW1lIGBhbGxfdm93ZWxzYC4KCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMgZ2V0IGZvcm1hbnQgdmFsdWVzIGZvciB0aG9zZSBzZWdtZW50cwojIGVpdGhlciBmcm9tIHByZS1nZW5lcmF0ZWQgUHJhYXQtVHJhY2sKIyB0ZF92b3dlbHMgPSBnZXRfdHJhY2tkYXRhKGRiLCBhbGxfdm93ZWxzLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHNzZmZUcmFja05hbWUgPSAicHJhYXRGbXMiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdFR5cGUgPSAidGliYmxlIikKYGBgCgpBcyBpdCB0YWtlcyBsb25nIHRvIGRvIHRoaXMsIHRoZSBkYXRhZnJhbWUgaGFzIGJlZW4gcHJlY29tcGlsZWQgYW5kIHNhdmVkIHRvIGZpbGUgLi4uIGFuZCByZWxvYWRlZCBhZ2Fpbi4KCmBgYHtyIGVjaG89VFJVRX0KI3NhdmVSRFModGRfdm93ZWxzLCBmaWxlID0idGRfdm93ZWxzLlJEUyIpCiMgdGRfdm93ZWxzIDwtIHJlYWRSRFMoInRkX3Zvd2Vscy5SRFMiKQpgYGAKCkR1ZSB0byB0aGUgdmFyeWluZyBsZW5ndGggb2YgYWxsIHZvd2VscywgaXQgaXMgbmVjZXNzYXJ5IHRvIG5vcm1hbGl6ZSB0aGUgbGVuZ3RoIHdpdGggYG5vcm1hbGl6ZV9sZW5ndGhgLiBUaGlzIHN0ZXAgdGFrZXMgbG9uZyBhbmQsIGFnYWluLCB0aGUgZGF0YWZyYW1lIGhhcyBhbHJlYWR5IGJlZW4gY3JlYXRlZCBhbmQgc2F2ZWQuCgpgYGB7cn0KIyBub3JtYWxpc2UgdGhlIGxlbmd0aAojdGRfdm93ZWxzX25vcm0gPSBub3JtYWxpemVfbGVuZ3RoKHRkX3Zvd2VscyAlPiUgc2VsZWN0KC1kdXJhdGlvbiwgLWxlbmd0aCksIGNvbE5hbWVzID0gYygiVDEiLCAiVDIiKSkKCiMgZG8gc29tZSBjbGVhbmluZwojIGV4Y2x1ZGUgd29yZHMgc3Bva2VuIGJ5IGFub3RoZXIgKG1hbGUpIHNwZWFrZXIKIyB0ZF92b3dlbHNfbm9ybSA8LSB0ZF92b3dlbHNfbm9ybSAlPiUKIyAgIGZpbHRlcighc3RyX2RldGVjdChidW5kbGUsICJeW0NEREVGXS4qIikpCgojIHNhdmUgdGhpcyBkYXRhZnJhbWUgCiNzYXZlUkRTKHRkX3Zvd2Vsc19ub3JtLCBmaWxlID0idGRfdm93ZWxzX25vcm0uUkRTIikKCiMgbG9hZCB0aGlzIGRhdGEgZnJhbWUKdGRfdm93ZWxzX25vcm0gPC0gcmVhZFJEUygidGRfdm93ZWxzX25vcm0uUkRTIikKYGBgCgpGb3JtYW50cyBhcmUgY2FsY3VsYXRlZCBmb3IgZXZlcnkgc2FtcGxpbmcgcG9pbnQgb2YgdGhlIGF1ZGlvIGZpbGUuIFRodXMsIHRoZSBkYXRhZnJhbWUgYHRkX3Zvd2Vsc19ub3JtYCBub3cgY29udGFpbnMgc29tZSAyLDEgbWlsbGlvbiByb3dzL3ZhbHVlcy4KCkZvciBpbGx1c3RyYXRpb24gcHVycG9zZXMsIHRoZSBmb3JtYW50cyBmb3IgdGhlIGxvbmcgdm93ZWxzIGluIHRocmVlIGV4YW1wbGUgd29yZHMgYXJlIHNlbGVjdGVkIGludG8gYSBkYXRhZnJhbWUgKCptYWFjaGVuLCBWaXosIEt1dXNjaHQqKS4KCmBgYHtyfQpmb3JtYW50X2V4YW1wbGVzIDwtIHRkX3Zvd2Vsc19ub3JtICU+JQogIGZpbHRlcihzbF9yb3dJZHggPT0gIjcyMiIgfCBzbF9yb3dJZHggPT0gIjIxNzAyIiB8IHNsX3Jvd0lkeCA9PSAiMTExOTEiKQpgYGAKClRoZXNlIHRocmVlIHZvd2VscyB0aGVuIGFyZSB2aXN1YWxpc2VkIGFzIGxpbmUgY2hhcnRzLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KZ2dwbG90KGRhdGE9Zm9ybWFudF9leGFtcGxlcykgKwogIGdlb21fc21vb3RoKGFlcyh4ID0gdGltZXNfbm9ybSwgeSA9IFQxLCBjb2wgPSBsYWJlbHMsIGdyb3VwID0gbGFiZWxzKSkgKwogIGdlb21fc21vb3RoKGFlcyh4ID0gdGltZXNfbm9ybSwgeSA9IFQyLCBjb2wgPSBsYWJlbHMsIGdyb3VwID0gbGFiZWxzKSkgKwogIGxhYnMoeCA9ICJEdXJhdGlvbiAobm9ybWFsaXplZCkiLCB5ID0gIkYxICsgRjIgKEh6KSIpICsKICBnZ3RpdGxlKCJFeGFtcGxlIGZvcm1hbnRzIEYxLCBGMiB0aGUgdGhyZWUgY29ybmVyIHZvd2VscyIpICsKICBmYWNldF93cmFwKH4gbGFiZWxzKQpgYGAKClRvIGdldCB0aGUgaWRlbnRpdHkgb2YgYSB2b3dlbCwgaXQgaXMgY29tbW9uIHByYWN0aWNlIHRvIHNlbGVjdCBvbmx5IG9uZSBwb2ludCBpbiB0aGUgbWlkZGxlIG9mIHRoZSB2b3dlbC4gVG8gZG8gc28sIEkgYW0gdXNpbmcgYSBgZmlsdGVyYCBhbmQgc2VsZWN0IHRoZSBGMSBhbmQgRjIgdmFsdWVzIGF0IHRoZSBub3JtYWxpc2VkIHRpbWUgcG9pbnQgYDAuNGAuIFRoaXMgZ2l2ZXMgdXMgRjEgYW5kIEYyIHZhbHVlcyBmb3IgMTA0LDA1NSB2b3dlbHMuCgpgYGB7cn0Kdm93ZWxfbWlkcG9pbnRzID0gdGRfdm93ZWxzX25vcm0gJT4lIAogIGZpbHRlcih0aW1lc19ub3JtID09IDAuNCkKdm93ZWxfbWlkcG9pbnRzJGxhYmVscyA8LSAgIGFzLmZhY3Rvcih2b3dlbF9taWRwb2ludHMkbGFiZWxzKQpgYGAKCkFnYWluLCBzb21lIGZ1cnRoZXIgZGF0YSBjb252ZXJzaW9uLgoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBjb252ZXJ0IEhlcnR6IHRvIEJhcmsKIyBjb252ZXJ0IFQxLCBUMiwgVDMgdG8gQmFyawp0ZF92b3dlbHNfbm9ybSRUMSA8LSBlbXVSOjpiYXJrKHRkX3Zvd2Vsc19ub3JtJFQxKQp0ZF92b3dlbHNfbm9ybSRUMiA8LSBlbXVSOjpiYXJrKHRkX3Zvd2Vsc19ub3JtJFQyKQoKIyBpbnNlcnQgbmV3IGNvbHVtbnMgZnJvbSByZXF1ZXJpZXMgdG8gaGF2ZSB0aGUgaW5mbyBhYm91dCB0aGUgbmV4dCBhbmQgbmV4dF9uZXh0IGFuZCBwcmV2aW91cyBsYWJlbCBhZnRlciB0aGUgdm93ZWwgYXZhaWxhYmxlIGluIHRoZSBkYXRhZnJhbWUKIyB2b3dlbF9taWRwb2ludHMkbmV4dF9sYWJlbCA9IGFzLmZhY3RvcihyZXF1ZXJ5X3NlcShkYiwgc2VnbGlzdCA9IHNsLCBvZmZzZXQ9MSkkbGFiZWxzKQojIHZvd2VsX21pZHBvaW50cyRuZXh0X25leHRfbGFiZWwgPSBhcy5mYWN0b3IocmVxdWVyeV9zZXEoZGIsIHNlZ2xpc3QgPSBzbCwgb2Zmc2V0PTIsIGlnbm9yZU91dE9mQm91bmRzID1UUlVFKSRsYWJlbHMpCiMgdm93ZWxfbWlkcG9pbnRzJHdvcmQgPSByZXF1ZXJ5X2hpZXIoZGIsIHNlZ2xpc3QgPSBzbCwgbGV2ZWw9Ik9SVCIpJGxhYmVscwojIHZvd2VsX21pZHBvaW50cyRwcmV2aW91c19sYWJlbCA8LSBhcy5mYWN0b3IocmVxdWVyeV9zZXEoZGIsIHNlZ2xpc3QgPSBzbCwgb2Zmc2V0PSAtMSkkbGFiZWxzKQojIAojIHNhdmVSRFModm93ZWxfbWlkcG9pbnRzLCAidm93ZWxfbWlkcG9pbnRzLlJEUyIpCnZvd2VsX21pZHBvaW50cyA8LSByZWFkUkRTKCJ2b3dlbF9taWRwb2ludHMuUkRTIikKYGBgCgpUaGUgZGF0YWZyYW1lIGB2b3dlbF9taWRwb2ludHNgIGlzIG5vdyBlbnJpY2hlZCB3aXRoIGFkZGl0aW9uYWwgY29sdW1ucyBmb3IgdGhlIGFjdHVhbCBgd29yZGAgKG9mIGEgdm93ZWwgaW5zdGFuY2UpLCBgcHJldmlvdXNfbGFiZWxgLCBgbmV4dF9sYWJlbGAgYW5kIGBuZXh0X25leHRfbGFiZWxgLCB3aGljaCBhbGxvd3MgdG8gc2VsZWN0IGRhdGEgbW9yZSBmaW5lLWdyYWluZWQgZm9yIHRoZSBmb2xsb3dpbmcgdmlzdWFsaXNhdGlvbnMsIGUuZy4gYWxsIHZvd2VscyBgYcuQYCB3aXRoIGEgYGdgIGluIGZyb250IGFuZCBhbiBgUmAgYWZ0ZXIgaXQuCgpgYGB7cn0KaGVhZCh2b3dlbF9taWRwb2ludHMpCmBgYAoKUHJlcGFyZWQgaW4gc3VjaCBhIHdheSwgdGhlIHZpc3VhbGlzYXRpb25zIG9mIHRoaXMgc2hlZXIgYW1vdW50IG9mICsxMDAsMDAwIHZvd2VscyBjYW4gY29tbWVuY2UuIFZvd2VsIHF1YWxpdHkgaXMgY2hhcnRlZCBpbiBzY2F0dGVyIHBsb3RzLCB3aGVyZSB0aGUgRjEgdmFsdWUgaXMgcGxvdHRlZCBvbiB0aGUgeS1heGlzIGFuZCB0aGUgRjIgdmFsdWUgb24gdGhlIHktYXhpcy4gVG8gZ2FpbiByZXNlbWJsYW5jZSB3aXRoIHRoZSBhcnRpY3VsYXRpb24gb2Ygdm93ZWxzLCBib3RoIGF4ZXMgYXJlIHJldmVyc2VkLiBUaHVzIGBpYCBpcyBsb2NhdGVkIGluIHRoZSB0b3AgZnJvbnQsIGB1YCBpbiB0aGUgdG9wIGJhY2sgYW5kIGBhYCBpbiB0aGUgbG93IHBvc2l0aW9uLgoKVGhlIHJlc3VsdCBsb29rcyByYXRoZXIgZmxhc2h5LCBidXQgbmV2ZXJ0aGVsZXNzIGhhcyBzb21lIGZsYXdzLiBGaXJzdCwgdGhlcmUgYXJlIHNldmVyYWwgb3V0bGllcnMsIGVzcGVjaWFsbHkgaW4gdGhlIFNXIGNvcm5lci4gQW4gZWxpbWluYXRpb24gcHJvY2VkdXJlIGZvciBvdXRsaWVycyBpcyBuZWVkZWQuIFNlY29uZGx5LCB0aGUgc2luZ2xlIGNsb3VkcyBmb3IgZWFjaCB2b3dlbHMgYXJlIG92ZXJsYXBwaW5nIGVhY2ggb3RoZXIgdG8gc3VjaCBhbiBleHRlbnQgdGhhdCB0aGUgcHJvcGVyIGxvY2F0aW9uIGlzIG5vdCBpZGVudGlmaWFibGUuCgpgYGB7cn0KIyB0YWtlcyB0b28gbG9uZzogZG8gbm90IHJ1biB3aXRoIGZ1bGwgZGF0YXNldCEKIyBnZ3Bsb3Qodm93ZWxfbWlkcG9pbnRzKSArCiMgICBhZXMoeCA9IFQyLCB5ID0gVDEsIGxhYmVsID0gbGFiZWxzLCBjb2wgPSBsYWJlbHMpICsKIyAgIGdlb21fdGV4dCgpICsKIyAgIHNjYWxlX3lfcmV2ZXJzZSgpICsgCiMgICBzY2FsZV94X3JldmVyc2UoYnJlYWtzID0gc2VxKGZyb209NywgdG89MTQsIGJ5PTEpLCBtaW5vcl9icmVha3MgPSBOVUxMKSArIAojICAgbGFicyh4ID0gIkYyIChIeikiLCB5ID0gIkYxIChIeikiKSArCiMgICBnZ3RpdGxlKCJTY2F0dGVyIHBsb3Qgb2YgYWxsIHZvd2VscyBpbiB0aGUgTE9EIGRhdGFiYXNlIikgCgojICBnZ3NhdmUoInBsb3Rfdm93ZWxfbWlkcG9pbnRzLnBuZyIpCmBgYAoKIVtWb3dlbCBwbG90IG9mIGFsbCB2b3dlbHMgaW4gdGhlIExPRCBkYXRhYmFzZV0ocGxvdF92b3dlbF9taWRwb2ludHMucG5nKQoKTmV4dCB0YXNrOiByZW1vdmUgb3V0bGllcnMuIEkgYW0gdXNpbmcgYSBmdW5jdGlvbiBiYXNlZCBvbiB0aGUgKGBNYWhhbG5vYmlzIGRpc3RhbmNlYClcWzxodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NYWhhbGFub2Jpc19kaXN0YW5jZT5cXSwgd2hpY2ggaW5jcmVtZW50YWxseSBleGNsdWRlcyBvdXRsaWVycyB1cCB0byBhIHByZWRlZmluZWQgdGhyZXNob2xkLiBJbiB0aGUgcHJlc2VudCBjYXNlIHdlIGtlZXAgODUgJSBvZiB0aGUgdmFsdWVzLgoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBwcmVyZXVuIQojIHdpdGhvdXRfb3V0bGllcnMgPC0gdm93ZWxfbWlkcG9pbnRzICU+JQojICAgc2VsZWN0KHdvcmQsIHByZXZpb3VzX2xhYmVsLCBsYWJlbHMsIG5leHRfbGFiZWwsIG5leHRfbmV4dF9sYWJlbCwgVDEsIFQyLCB0aW1lc19yZWwpICU+JQojICAgZ3JvdXBfYnkobGFiZWxzKSAlPiUKIyAgIGZpbHRlcighZmluZF9vdXRsaWVycyhUMSwgVDIsIHRpbWVzX3JlbCwga2VlcCA9IDAuODUpKQpgYGAKCmBgYHtyfQojIG91dGxpZXIgY29tcHV0YXRpb25zIHRha2VzIGFsc28gYSB3aGlsZQojIHRvIGJlIG9uIHRoZSBzYWZlIHNpZGUsIGFsc28gc2F2ZSB0aGlzIGRhdGFmcmFtZSB0byBkaXNrCiNzYXZlUkRTKHdpdGhvdXRfb3V0bGllcnMsIGZpbGU9IndpdGhvdXRfb3V0bGllcnMuUkRTIikKd2l0aG91dF9vdXRsaWVycyA8LSByZWFkUkRTKCJ3aXRob3V0X291dGxpZXJzLlJEUyIpCmBgYAoKYHdpdGhvdXRfb3V0bGllcnNgIGNvbnRhaW5zIHNvbWUgODgsMDAwIHJvd3MsIG1lYW5pbmcgbmVhcmx5IDIwLDAwMCBoYXZlIGJlZW4gaWRlbnRpZmllZCBhbmQgcmVtb3ZlZCBhcyBvdXRsaWVycy4KClRvIHJlZHVjZSB0aGUgYW1vdW50IG9mIGRhdGEgcG9pbnRzIGluIHRoZSBzY2F0dGVyIHBsb3QsIG9uZSBjYW4gZHJhdyBgY29udG91ciBwbG90c2AgKGZvbGxvd2luZyBbdGhpcyBpZGVhXShodHRwOi8vY2hyaXN0aWFuZGljYW5pby5ibG9nc3BvdC5jb20vMjAxMy8xMC92aXN1YWxpemluZy12b3dlbC1zcGFjZXMtaW4tci1mcm9tLmh0bWwpKS4gVGhpcyBtZXRob2QgYGdlb21fZGVuc2l0eTJkYCBmYWNpbGl0YXRlcyB0byBpZGVudGlmeSB0aGUgY29yZSB6b25lcyBvZiB2b3dlbCByZWFsaXNhdGlvbnMsIGkuZS4gd2hlcmUgLSBzaW1pbGFyIHRvIGEgd2VhdGhlciBjaGFydCAtIHRoZSBsaW5lcyBhcmUgY2xvc2VyIHRvZ2V0aGVyLgoKYGBge3J9CmdncGxvdCh3aXRob3V0X291dGxpZXJzLCBhZXMoeCA9IFQyLCB5ID0gVDEsIGNvbG9yPWxhYmVscykpICsKICBnZW9tX2RlbnNpdHkyZChhZXMobGFiZWw9IGxhYmVscykpICsKICBzY2FsZV95X3JldmVyc2UoKSArIAogIHNjYWxlX3hfcmV2ZXJzZShicmVha3MgPSBzZXEoZnJvbT03LCB0bz0xNCwgYnk9MSksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsgCiAgbGFicyh4ID0gIkYyIChCYXJrKSIsIHkgPSAiRjEgKEJhcmspIikgKwogIGdndGl0bGUoIkNvbnRvdXIgcGxvdCBvZiBhbGwgdm93ZWxzIGluIHRoZSBMT0QgZGF0YWJhc2UiKQpgYGAKCkluc3RlYWQgb2YgZGlzcGxheWluZyBlYWNoIGFuZCBldmVyeSBkYXRhIHBvaW50LCBkcmF3IG9ubHkgYW4gZWxsaXBzZSBmb3IgdGhlIGV4dGVudCBvZiBhIHZvd2VsIGNsb3VkLiBGb3IgZWFjaCBlbGxpcHNlIHRoZSBtaWRkbGUgcG9pbnQsIHRoZSBzby1jYWxsZWQgYGNlbnRyb2lkYCBpcyBhbHNvIGNvbXB1dGVkLgoKYGBge3J9CiMgcGxvdCBjZW50cm9pZHMgYW5kIGVsbGlwc2VzCmNlbnRyb2lkID0gd2l0aG91dF9vdXRsaWVycyAlPiUKICBncm91cF9ieShsYWJlbHMpICU+JQogIHN1bW1hcml6ZShGMT1tZWFuKFQxKSwgRjI9bWVhbihUMikpCgpnZ3Bsb3Qod2l0aG91dF9vdXRsaWVycykgKwogIGFlcyh4PVQyLHk9VDEsIGNvbD1sYWJlbHMsbGFiZWw9bGFiZWxzKSsKICBzdGF0X2VsbGlwc2UoKSArCiAgZ2VvbV90ZXh0KGRhdGE9Y2VudHJvaWQsYWVzKHg9RjIseT1GMSkpICsKICBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpICsKICBsYWJzKHggPSAiRjIgKEh6KSIsIHkgPSAiRjEgKEh6KSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSAgKwogIGdndGl0bGUoIkRpc3BlcnNpb24gb2Ygdm93ZWxzIGluIEx1eGVtYm91cmdpc2giKQpgYGAKCkEgZnVydGhlciBwb3NzaWJpbGl0eSBpcyB0byB1c2UgdGhlIGZ1bmN0aW9uIGBmYWNldF93cmFwYCB3aGljaCBjcmVhdGVzIHNpbmdsZSBwbG90IGZvciB0aGUgZmFjdG9ycyBvZiBhIHZhcmlhYmxlLiBIZXJlLCBpbmRpdmlkdWFsIHBsb3RzIGFyZSBkaXNwbGF5ZWQgYWNjb3JkaW5nIHRvIHRoZSBsYWJlbHMgKCA9IHZvd2VscykgaW4gdGhlIGRhdGFmcmFtZS4gVGhlIGVsbGlwdG9pZCBzaGFwZSBvZiBlYWNoIGNsb3VkIGlzIGEgcmVzdWx0IG9mIHRoZSBvdXRsaWVyIHJlbW92YWwgcHJvY2VkdXJlLgoKYGBge3J9CmdncGxvdCh3aXRob3V0X291dGxpZXJzICU+JSBmaWx0ZXIoIShsYWJlbHMgPT0iyZAiIHwgbGFiZWxzID09ICfJmScgfCBsYWJlbHMgPT0gJ8mUy5AnKSkpICsKICBhZXMoeCA9IFQyLCB5ID0gVDEsIGxhYmVsID0gbGFiZWxzLCBjb2wgPSBsYWJlbHMpICsKICBnZW9tX2RlbnNpdHkyZChhZXMobGFiZWw9IGxhYmVscykpICsKICAjZ2VvbV90ZXh0KCkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsgCiAgc2NhbGVfeF9yZXZlcnNlKGJyZWFrcyA9IHNlcShmcm9tPTcsIHRvPTE0LCBieT0xKSwgbWlub3JfYnJlYWtzID0gTlVMTCkgKyAKICBsYWJzKHggPSAiRjIgKEJhcmspIiwgeSA9ICJGMSAoQmFyaykiKSArCiAgZ2d0aXRsZSgiU2NhdHRlciBwbG90IG9mIGFsbCB2b3dlbHMgaW4gdGhlIExPRCBkYXRhYmFzZSIpICsKICBmYWNldF93cmFwKH4gbGFiZWxzKQpgYGAKCiMgU291bmQgY2hhbmdlIGluIHByb2dyZXNzOiBUaGUgb3ZlcmxhcCBvZiB2b3dlbHMKCkZyb20gdGhlIHZpc3VhbGlzYXRpb24gc2hvd24gc28gZmFyIGl0IGJlY29tZXMgb2J2aW91cyB0aGF0IHZvd2VscyBhcmUgbm90IG5lYXRseSBzZXBhcmF0ZWQgYnV0IHJhdGhlciBvZnRlbiBzaG93IGEgY2VydGFpbiBhbW91bnQgb2Ygb3ZlcmxhcC4gVGhpcyBpcyBlaXRoZXIgZHVlIHRvIG1lYXN1cmVtZW50IGVycm9ycyBvciBpcyBpbmRpY2F0aW5nIHRoYXQgYSB2b3dlbCBwcm9kdWN0aW9uIGlzIGNoYW5naW5nLCBpLmUuIHRoYXQgd2UgYXJlIGRlYWxpbmcgd2l0aCBzb3VuZCBjaGFuZ2UuIFNvdW5kIGNoYW5nZSB1c3VhbGx5IG1hbmlmZXN0cyBpdHNlbGYgaW4gc3ViY29uc2Npb3VzLCBtaW5pbWFsIGRldmlhdGlvbnMgZnJvbSBhIGZvcm1lciBwcm90b3R5cGljYWwgc291bmQgYW5kIHRoZXNlIGRldmlhdGlvbiBtYXkgYmVjb21lIG1vcmUgcHJvbWluZW50IGF0IGEgbGF0ZXIgc3RhZ2UuCgpJbiBMdXhlbWJvdXJnaXNoLCB0b28sIHNldmVyYWwgc291bmQgY2hhbmdlcyBhcmUgaW4gcHJvZ3Jlc3MgZm9yIHRoZSB2b3dlbHMuIFRoZSBsYXN0IGV4YW1wbGUgaW4gdGhpcyB0dXRvcmlhbCBjb3ZlcnMgdGhlIHZhcmlhdGlvbmFsIHBhdHRlcm4gb2YgdGhlIHR3byBlLXZvd2VscywgXFtlXF0gKGluIHdvcmRzIGxpa2UgKk3DqWNrLCBicsOpbmdlbiwgc8OpY2hlciopIGFuZCBcW8mZXF0gKGxpa2UgaW4gKlNjaMOrZmYsIETDq3NjaCopLiBXaGlsZSBmb3IgZWxkZXIgc3BlYWtlcnMgdGhlc2Ugdm93ZWxzIGFyZSBkaXN0aW5jdCwgdGhlcmUgaXMgYW4gb25nb2luZyBtZXJnZXIgZm9yIHlvdW5nZXIgc3BlYWtlciBieSBwcm9ub3VuY2luZyB0aGUgdm93ZWwgYmVmb3JlIHRoZSBjZXJ0YWluIGNvbnNvbmFudHMgbGlrZSBcW8mZXF0gaW5zdGVhZCBvZiBcW2VcXS4KClRvIHRhY2tsZSB0aGlzIHRhc2ssIHdlIGZpcnN0IG5lZWQgdGhlIHJlbGV2YW50IGRhdGEsIHdoaWNoIGhhcyBiZWVuIHByZXBhcmVkIGluIGEgZGF0YWZyYW1lIGBtZXJnZXJgLgoKYGBge3J9CmVfbWVyZ2VyX2RhdGEgPC0gd2l0aG91dF9vdXRsaWVycyAlPiUKICBmaWx0ZXIobGFiZWxzICVpbiUgYygiZSIsICLJmSIpKSAlPiUKICBmaWx0ZXIobmV4dF9sYWJlbCAlaW4lIGMoIsmVIiwgIsqDIikpCgpzYXZlUkRTKGVfbWVyZ2VyX2RhdGEsICJlX21lcmdlcl9kYXRhLlJEUyIpCm1lcmdlciA8LSByZWFkUkRTKCJlX21lcmdlcl9kYXRhLlJEUyIpCmBgYAoKV2l0aCBhcm91bmQgMiwxMDAgcm93cyB0aGlzIGRhdGFzZXQgaXMgbm90IHRvbyBsYXJnZSBmb3IgYSBzY2F0dGVyIHBsb3QuIFRoZSByZWxldmFudCBpbmZvcm1hdGlvbiBpcyB0aGUgaG9yaXpvbnRhbCBvdmVybGFwIGJldHdlZW4gdGhlIGNsb3Vkcy4gV2hpbGUgdHdvIG9mIHRoZSByZWQgb25lcyAoXFtrXF0sIFxbxYtcXSkgYXJlIHF1aXRlIGRpc3RpbmN0IGZyb20gdGhlIGdyZWVuIG9uZSwgaXQgaXMgdGhlIGNsb3VkIGZvciBcW8mVXF0gd2hpY2ggaXMgc2hvd2luZyBvdmVybGFwIHdpdGggdGhlIGNsb3VkIGZvciBcW8qDXF0uIEhlcmUgd2UgaGF2ZSBjbGVhciBldmlkZW5jZSBvZiBhbiBvbmdvaW5nIHZvd2VsIG1lcmdlciwgaS5lLiB0aGUgZS12b3dlbCByZWFsaXNhdGlvbnMgYmVmb3JlIFxbyZVcXSBhbmQgXFvKg1xdIGFyZSBiZWNvbWluZyBtb3JlIHNpbWlsYXIuCgpgYGB7cn0KZ2dwbG90KG1lcmdlcikgKwogIGFlcyh4ID0gVDIsIHkgPSBUMSwgbGFiZWwgPSBsYWJlbHMsIGNvbCA9IGxhYmVscykgKwogIGdlb21fdGV4dCgpICsKICBzY2FsZV95X3JldmVyc2UoKSArIAogIHNjYWxlX3hfcmV2ZXJzZShicmVha3MgPSBzZXEoZnJvbT03LCB0bz0xNCwgYnk9MSksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsgCiAgbGFicyh4ID0gIkYyIChCYXJrKSIsIHkgPSAiRjEgKEJhcmspIikgKwogIGdndGl0bGUoIlNjYXR0ZXIgcGxvdCBvZiB0aGUgdm93ZWxzIFvJmV0gYW5kIFtlXSBpbiB0aGUgTE9EIGRhdGFiYXNlIikgKwogIGZhY2V0X3dyYXAofiBuZXh0X2xhYmVsKQpgYGAKClRoZSBhY3R1YWwgYW1vdW50IG9mIG92ZXJsYXAgYmV0d2VlbiB0d28gY2xvdWQtbGlrZSBkYXRhIHNldHMgY2FuIGJlIGNhbGN1bGF0ZWQgd2l0aCB0aGUgc28tY2FsbGVkIGBQaWxsYWlgIHNjb3JlIERldGFpbHMgc2VlIFtoZXJlXShodHRwczovL2pvZXlzdGFubGV5LmNvbS9ibG9nL2EtdHV0b3JpYWwtaW4tY2FsY3VsYXRpbmctdm93ZWwtb3ZlcmxhcCkuCgpCYXNpY2FsbHksIGhpZ2ggUGlsbGFpIHNjb3JlcyBpbmRpY2F0ZSBkaXN0aW5jdCBjbG91ZHMsIGxvdyBQaWxsYWkgc2NvcmUgaW5kaWNhdGUgbWVyZ2VyLiAoW1NvdXJjZSBvZiBmaWd1cmVdKGh0dHBzOi8vam9leXN0YW5sZXkuY29tL2Jsb2cvYS10dXRvcmlhbC1pbi1jYWxjdWxhdGluZy12b3dlbC1vdmVybGFwKSkKCmBgYHtyIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHJlcCgicGlsbGFpX2V4YW1wbGUucG5nIikpCmBgYAoKTGV0J3Mgbm93IGNhbGN1bGF0ZSB0aGUgUGlsbGFpIHNjb3JlcyBmb3IgdGhlIG92ZXJsYXAuIFdlIGV4dHJhY3QgdGhlIHJvd3MgZm9yIFxbZVxdIGJlZm9yZSBcW8mVXF0gYW5kIFxbyZlcXSBiZWZvcmUgXFvKg1xdLCByZXNwZWN0aXZlbHksIGFuZCBtZXJnZSB0aGVzZSB0byBhIHRlbXBvcmFyeSBkZiAndm93ZWxzJy4KCmBgYHtyfQojIHNlbGVjdCByb3dzIGZvciB0aGUgdHdvIHNvdW5kcwplX2NoX3Zvd2VscyA8LSBtZXJnZXIgJT4lCiAgZmlsdGVyKChsYWJlbHMgPT0gImUiICYgbmV4dF9sYWJlbCA9PSLJlSIpKQogICAgICAgICAKZV9zY2hfdm93ZWxzIDwtIG1lcmdlciAlPiUKICBmaWx0ZXIoKGxhYmVscyA9PSAiyZkiICYgbmV4dF9sYWJlbCA9PSLKgyIpKQoKIyBuZXcgZGYgd2l0aCB0aGUgc2VsZWN0ZWQgcm93cwp2b3dlbHMgPC0gcmJpbmQoZV9jaF92b3dlbHMsIGVfc2NoX3Zvd2VscykKCiMgY2FsY3VsYXRlIHRoZSBQaWxsYWkgc2NvcmUKcGlsbGFpKGNiaW5kKFQxLCBUMikgfiBsYWJlbHMsIGRhdGEgPSB2b3dlbHMpCiAgICAgICAgIApgYGAKClJlbWVtYmVyLCB0aGUgUGlsbGFpIHNjb3JlIHJhbmdlcyBiZXR3ZWVuIDAgYW5kIDEuIFRoZSBsb3dlciB0aGUgc2NvcmUsIHRoZSBjbG9zZXIgdHdvIGdyb3VwcyBvZiByZWFsaXNhdGlvbnMuIFdpdGggdGhpcyByYXRoZXIgbG93IHZhbHVlIHRoZSBQaWxsYWkgc2NvcmUgaW1wbGllcyB0aGF0IHRoZSB0d28gdm93ZWwgcmVhbGlzYXRpb25zIGFyZSBpbmRlZWQgcmF0aGVyIGNsb3NlIGFuZCBtZXJnaW5nIGludG8gZWFjaCBvdGhlci4KCiMgVm93ZWwgZXhwbG9yZXIgKFNoaW55IGFwcCkKClRyeSBpdCBbaGVyZV0oaHR0cHM6Ly9wZXRlcmdpbGwuc2hpbnlhcHBzLmlvL3NoaW55cGxheS8pLgoKWyFbVm93ZWwgZXhwbG9yZXJdKFZvd2VsJTIwZXhwbG9yZXIucG5nKV0oaHR0cHM6Ly9wZXRlcmdpbGwuc2hpbnlhcHBzLmlvL3NoaW55cGxheS8pCg==